package fei.typhoon.engine.utils;

import java.io.UnsupportedEncodingException;

public class FeiBase64 {

	private static FeiBase64 instance = null;

//	private static byte encodeTable[] = { 119, 77, 121, 112, 75, 113, 81, 87, 80, 115,
//			49, 89, 111, 67, 106, 102, 122, 60, 90, 97, 66, 68, 95, 109, 72,
//			104, 120, 88, 99, 74, 50, 48, 69, 114, 83, 53, 52, 103, 108, 100,
//			82, 57, 62, 98, 76, 56, 44, 86, 51, 73, 70, 101, 116, 55, 71, 79,
//			85, 110, 54, 78, 118, 84, 117, 105 };
	private static byte decodeTable[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, 46, -1, -1, -1, 31, 10, 30, 48, 36, 35, 58, 53, 45, 41, -1, -1,
			17, -1, 42, -1, -1, -1, 20, 13, 21, 32, 50, 54, 24, 49, 29, 4, 44,
			1, 59, 55, 8, 6, 40, 34, 61, 56, 47, 7, 27, 11, 18, -1, -1, -1, -1,
			22, -1, 19, 43, 28, 39, 51, 15, 37, 25, 63, 14, -1, 38, 23, 57, 12,
			3, 5, 33, 9, 52, 62, 60, 0, 26, 2, 16, -1, -1, -1, -1, -1 };

	private static char fill2 = 'A';
	private static char fill1 = 'k';

	public static FeiBase64 get() {
		if (instance == null) {
			instance = new FeiBase64();
		}
		return instance;
	}

	public static boolean isValidBase64Byte(byte b) {
		if (b == fill2 || b == fill1) {
			return true;
		} else if ((b < 0) || (b >= 128)) {
			return false;
		} else if (decodeTable[b] == -1) {
			return false;
		}
		return true;
	}

	public static String decodeToString(String data) {
		try {
			byte[] content = get().base64Decode(data.getBytes());
			if (content != null) {
				return (new String(content, "UTF-8"));
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
	}

	public static String decodeToString(byte... data) {
		try {
			byte[] content = get().base64Decode(data);
			if (content != null) {
				return (new String(content, "UTF-8"));
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
	}

	public static byte[] decodeToBytes(String data) {
		byte[] content = get().base64Decode(data.getBytes());
		return content;
	}

	public static byte[] decodeToBytes(byte... data) {
		byte[] content = get().base64Decode(data);
		return content;
	}

	private byte[] decode(byte[] data) {
		if (data == null) {
			return null;
		}
		byte b1, b2, b3, b4;
		int dataLength = data.length, bytesLength = 0, i = 0, j = 0;
		for (; i < dataLength; i++) {
			if (!isValidBase64Byte(data[i])) {
				dataLength = 0;
				System.out.println("decode input data wrong, data[" + i + "]="
						+ data[i]);
				return null;
			}
		}

		if (data[dataLength - 2] == fill2) {
			bytesLength = (((dataLength / 4) - 1) * 3) + 1;
		} else if (data[dataLength - 1] == fill1) {
			bytesLength = (((dataLength / 4) - 1) * 3) + 2;
		} else {
			bytesLength = (dataLength / 4) * 3;
		}
		byte[] bytes = new byte[bytesLength];

		for (i = 0, j = 0; i < (dataLength - 4); i += 4, j += 3) {
			b1 = decodeTable[data[i]];
			b2 = decodeTable[data[i + 1]];
			b3 = decodeTable[data[i + 2]];
			b4 = decodeTable[data[i + 3]];
			bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[j + 2] = (byte) ((b3 << 6) | b4);
		}
		if (data[dataLength - 2] == fill2) {
			b1 = decodeTable[data[dataLength - 4]];
			b2 = decodeTable[data[dataLength - 3]];
			bytes[bytesLength - 1] = (byte) ((b1 << 2) | (b2 >> 4));
		} else if (data[dataLength - 1] == fill1) {
			b1 = decodeTable[data[dataLength - 4]];
			b2 = decodeTable[data[dataLength - 3]];
			b3 = decodeTable[data[dataLength - 2]];
			bytes[bytesLength - 2] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytesLength - 1] = (byte) ((b2 << 4) | (b3 >> 2));
		} else {
			b1 = decodeTable[data[dataLength - 4]];
			b2 = decodeTable[data[dataLength - 3]];
			b3 = decodeTable[data[dataLength - 2]];
			b4 = decodeTable[data[dataLength - 1]];
			bytes[bytesLength - 3] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytesLength - 2] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[bytesLength - 1] = (byte) ((b3 << 6) | b4);
		}
		return bytes;
	}

	private byte[] base64Decode(byte[] data) {
		return decode(data);
	}

}
